package ace.module.oauth2.server.core.impl.service.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.security.core.impl.constant.Oauth2Constant;
import ace.module.common.api.constant.CommonConstants;
import ace.module.common.api.enums.FrameworkModeEnum;
import ace.module.common.core.impl.properties.ModuleCommonBaseProperties;
import ace.module.common.core.impl.properties.ModuleCommonSecurityProperties;
import ace.module.oauth2.server.api.enums.Oauth2RegisteredClientAuthConfigApiImplTypeEnum;
import ace.module.oauth2.server.api.enums.Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum;
import ace.module.oauth2.server.api.enums.Oauth2RegisteredClientAuthConfigBizAuthTypeEnum;
import ace.module.oauth2.server.api.enums.Oauth2RegisteredClientAuthConfigFeignRequestInterceptorStatusEnum;
import ace.module.oauth2.server.api.enums.Oauth2RegisteredClientAuthConfigStatusEnum;
import ace.module.oauth2.server.core.impl.authentication.ace.service.AceAuthenticationService;
import ace.module.oauth2.server.core.impl.authentication.external.user.Oauth2ExternalUserAuthenticationConstant;
import ace.module.oauth2.server.core.impl.dao.entity.Oauth2RegisteredClientAuthConfig;
import ace.module.oauth2.server.core.impl.manager.Oauth2RegisteredClientAuthConfigManager;
import ace.module.oauth2.server.core.impl.service.Oauth2ServerInitService;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author caspar
 * @date 2023/3/29 14:20 初始化默认数据
 */
@AllArgsConstructor
@Slf4j
@Component
public class Oauth2ServerInitServiceImpl implements Oauth2ServerInitService {

  private final RegisteredClientRepository registeredClientRepository;
  private final Oauth2RegisteredClientAuthConfigManager oauth2RegisteredClientAuthConfigManager;
  private final ModuleCommonBaseProperties moduleCommonBaseProperties;
  private final ModuleCommonSecurityProperties moduleCommonSecurityProperties;
  private final AceAuthenticationService aceAuthenticationService;

  @Transactional(rollbackFor = Exception.class)
  public void init() {

    this.addAdminClient();

    this.addInternalServerClient();

    this.addProxyExternalUserClient();

  }

  /**
   * 默认支持外部用户客户端
   */
  private void addProxyExternalUserClient() {
    if (registeredClientRepository.findById(moduleCommonSecurityProperties.getOauth2ProxyExternalUserIdOfClient()) != null) {
      log.info("oauth2 代理内部服务客户端已经存在");
      return;
    }
    RegisteredClient registeredClient = RegisteredClient.withId(moduleCommonSecurityProperties.getOauth2ProxyExternalUserIdOfClient())
        .clientId(moduleCommonSecurityProperties.getOauth2ProxyExternalUserClientId())
        .clientIdIssuedAt(Instant.now())
        .clientSecret(moduleCommonSecurityProperties.getOauth2ProxyExternalUserClientSecret())
        .clientSecretExpiresAt(LocalDateTime.now().plusYears(100L).toInstant(ZoneOffset.UTC))
        .clientName("代理认证客户端")
        .clientAuthenticationMethods(
            clientAuthenticationMethods -> {
              clientAuthenticationMethods.addAll(
                  Arrays.asList(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
              );
            })
        .authorizationGrantTypes(
            authorizationGrantTypes -> {
              authorizationGrantTypes.addAll(
                  Arrays.asList(
                      AuthorizationGrantType.REFRESH_TOKEN,
                      Oauth2ExternalUserAuthenticationConstant.EXTERNAL_USER_AUTHORIZATION_GRANT_TYPE
                  )
              );
            })
        .redirectUri(moduleCommonSecurityProperties.getOauth2RedirectUri())
        .scopes(
            scopes -> {

            })
        .clientSettings(
            ClientSettings.builder()
                .requireProofKey(false)
                .requireAuthorizationConsent(false)
                .jwkSetUrl(StringUtils.EMPTY)
                .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)
                .build())
        .tokenSettings(
            TokenSettings.builder()
                .accessTokenFormat(OAuth2TokenFormat.REFERENCE)
                .reuseRefreshTokens(true)
                .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
                .accessTokenTimeToLive(Duration.ofMinutes(30))
                .refreshTokenTimeToLive(Duration.ofDays(7))
                .authorizationCodeTimeToLive(Duration.ofMinutes(5))
                .build())
        .build();

    registeredClientRepository.save(registeredClient);

    log.info("oauth2 代理内部服务客户端初始化成功");
  }

  /**
   * 默认管理员客户端
   */
  private void addAdminClient() {
    if (registeredClientRepository.findByClientId(moduleCommonSecurityProperties.getOauth2AdminClientId()) != null) {
      log.info("运营管理平台-oauth2管理平台客户端已经存在");
      return;
    }
    RegisteredClient registeredClient = RegisteredClient.withId(CommonConstants.DEFAULT_ADMIN_CLIENT_ID_OF_CLIENT)
        .clientId(moduleCommonSecurityProperties.getOauth2AdminClientId())
        .clientIdIssuedAt(Instant.now())
        .clientSecret(moduleCommonSecurityProperties.getOauth2AdminClientSecret())
        .clientSecretExpiresAt(LocalDateTime.now().plusYears(100L).toInstant(ZoneOffset.UTC))
        .clientName(moduleCommonSecurityProperties.getOauth2AdminClientId())
        .clientAuthenticationMethods(
            clientAuthenticationMethods -> {
              clientAuthenticationMethods.addAll(
                  Arrays.asList(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
              );
            })
        .authorizationGrantTypes(
            authorizationGrantTypes -> {
              authorizationGrantTypes.addAll(
                  Arrays.asList(
                      AuthorizationGrantType.REFRESH_TOKEN,
                      aceAuthenticationService.getAuthorizationGrantType(),
                      Oauth2ExternalUserAuthenticationConstant.EXTERNAL_USER_AUTHORIZATION_GRANT_TYPE
                  )
              );
            })
        .redirectUri(moduleCommonSecurityProperties.getOauth2RedirectUri())
        .scopes(
            scopes -> {
              scopes.addAll(Arrays.asList(Oauth2Constant.CLIENT_SCOPE_USERINFO_KEY));
            })
        .clientSettings(
            ClientSettings.builder()
                .requireProofKey(false)
                .requireAuthorizationConsent(false)
                .jwkSetUrl(StringUtils.EMPTY)
                .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)
                .build())
        .tokenSettings(
            TokenSettings.builder()
                .accessTokenFormat(OAuth2TokenFormat.REFERENCE)
                .reuseRefreshTokens(true)
                .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
                .accessTokenTimeToLive(Duration.ofMinutes(30))
                .refreshTokenTimeToLive(Duration.ofDays(7))
                .authorizationCodeTimeToLive(Duration.ofMinutes(5))
                .build())
        .build();

    Oauth2RegisteredClientAuthConfig beanAuthenticationConfig = Oauth2RegisteredClientAuthConfig.builder()
        .id(this.oauth2RegisteredClientAuthConfigManager.getNewId())
        .callbackUrl(StringUtils.EMPTY)
        .authenticationType(Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum.DEFAULT.getCode())
        .idOfClient(registeredClient.getId())
        .bizAuthType(Oauth2RegisteredClientAuthConfigBizAuthTypeEnum.AUTHENTICATION.getCode())
        .name("运营管理平台-客户端-认证-bean接口")
        .remark(StringUtils.EMPTY)
        .apiImplType(Oauth2RegisteredClientAuthConfigApiImplTypeEnum.BEAN.getCode())
        .beanName(StringUtils.EMPTY)
        .feignName(StringUtils.EMPTY)
        .priority(CoreConstant.ZERO_LONG)
        .feignRequestInterceptorStatus(Oauth2RegisteredClientAuthConfigFeignRequestInterceptorStatusEnum.NONE.getCode())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(Instant.now().toEpochMilli())
        .updateTime(Instant.now().toEpochMilli())
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    Oauth2RegisteredClientAuthConfig restAuthenticationConfig = Oauth2RegisteredClientAuthConfig.builder()
        .id(this.oauth2RegisteredClientAuthConfigManager.getNewId())
        .callbackUrl(this.moduleCommonSecurityProperties.getIamOauth2AuthenticationUrl())
        .authenticationType(Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum.DEFAULT.getCode())
        .idOfClient(registeredClient.getId())
        .bizAuthType(Oauth2RegisteredClientAuthConfigBizAuthTypeEnum.AUTHENTICATION.getCode())
        .name("运营管理平台-客户端-认证-rest接口")
        .remark(StringUtils.EMPTY)
        .apiImplType(Oauth2RegisteredClientAuthConfigApiImplTypeEnum.REST.getCode())
        .beanName(StringUtils.EMPTY)
        .feignName(this.moduleCommonSecurityProperties.getOauth2AdminClientAuthenticationFeignName())
        .priority(CoreConstant.ZERO_LONG)
        .feignRequestInterceptorStatus(Oauth2RegisteredClientAuthConfigFeignRequestInterceptorStatusEnum.INTERNAL_SERVER.getCode())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(Instant.now().toEpochMilli())
        .updateTime(Instant.now().toEpochMilli())
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    Oauth2RegisteredClientAuthConfig beanAuthorizationConfig = Oauth2RegisteredClientAuthConfig.builder()
        .id(this.oauth2RegisteredClientAuthConfigManager.getNewId())
        .callbackUrl(StringUtils.EMPTY)
        .idOfClient(registeredClient.getId())
        .authenticationType(Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum.UNKNOWN.getCode())
        .bizAuthType(Oauth2RegisteredClientAuthConfigBizAuthTypeEnum.AUTHORIZATION.getCode())
        .name("运营管理平台-客户端-授权-bean接口")
        .remark(StringUtils.EMPTY)
        .apiImplType(Oauth2RegisteredClientAuthConfigApiImplTypeEnum.BEAN.getCode())
        .beanName(StringUtils.EMPTY)
        .feignName(StringUtils.EMPTY)
        .priority(CoreConstant.ZERO_LONG)
        .feignRequestInterceptorStatus(Oauth2RegisteredClientAuthConfigFeignRequestInterceptorStatusEnum.NONE.getCode())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(Instant.now().toEpochMilli())
        .updateTime(Instant.now().toEpochMilli())
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    Oauth2RegisteredClientAuthConfig restAuthorizationConfig = Oauth2RegisteredClientAuthConfig.builder()
        .id(this.oauth2RegisteredClientAuthConfigManager.getNewId())
        .callbackUrl(this.moduleCommonSecurityProperties.getUpmsOauth2AuthorizationUrl())
        .authenticationType(Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum.UNKNOWN.getCode())
        .idOfClient(registeredClient.getId())
        .bizAuthType(Oauth2RegisteredClientAuthConfigBizAuthTypeEnum.AUTHORIZATION.getCode())
        .name("运营管理平台-客户端-授权-rest接口")
        .remark(StringUtils.EMPTY)
        .apiImplType(Oauth2RegisteredClientAuthConfigApiImplTypeEnum.REST.getCode())
        .beanName(StringUtils.EMPTY)
        .feignName(this.moduleCommonSecurityProperties.getOauth2AdminClientAuthorizationFeignName())
        .priority(CoreConstant.ZERO_LONG)
        .feignRequestInterceptorStatus(Oauth2RegisteredClientAuthConfigFeignRequestInterceptorStatusEnum.INTERNAL_SERVER.getCode())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(Instant.now().toEpochMilli())
        .updateTime(Instant.now().toEpochMilli())
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    Integer beanStatus = FrameworkModeEnum.SINGLE.equals(moduleCommonBaseProperties.getFrameworkMode()) ?
        Oauth2RegisteredClientAuthConfigStatusEnum.ENABLE.getCode() :
        Oauth2RegisteredClientAuthConfigStatusEnum.DISABLE.getCode();

    Integer restStatus = FrameworkModeEnum.REST.equals(moduleCommonBaseProperties.getFrameworkMode()) ?
        Oauth2RegisteredClientAuthConfigStatusEnum.ENABLE.getCode() :
        Oauth2RegisteredClientAuthConfigStatusEnum.DISABLE.getCode();

    beanAuthenticationConfig.setStatus(beanStatus);
    beanAuthorizationConfig.setStatus(beanStatus);

    restAuthenticationConfig.setStatus(restStatus);
    restAuthorizationConfig.setStatus(restStatus);

    this.registeredClientRepository.save(registeredClient);
    oauth2RegisteredClientAuthConfigManager.save(beanAuthenticationConfig, false);
    oauth2RegisteredClientAuthConfigManager.save(restAuthenticationConfig, false);
    oauth2RegisteredClientAuthConfigManager.save(beanAuthorizationConfig, false);
    oauth2RegisteredClientAuthConfigManager.save(restAuthorizationConfig, false);
    log.info("运营管理平台-oauth2管理平台客户端初始化成功");
  }


  /**
   * 添加内部服务客户端
   */
  private void addInternalServerClient() {
    if (registeredClientRepository.findById(CommonConstants.DEFAULT_INTERNAL_SERVER_CLIENT_ID_OF_CLIENT) != null) {
      log.info("oauth2 内部服务客户端已经存在");
      return;
    }
    RegisteredClient registeredClient =
        RegisteredClient.withId(CommonConstants.DEFAULT_INTERNAL_SERVER_CLIENT_ID_OF_CLIENT)
            .clientId(moduleCommonSecurityProperties.getOauth2InternalServerClientId())
            .clientSecret(moduleCommonSecurityProperties.getOauth2InternalServerClientSecret())
            .clientIdIssuedAt(Instant.now())
            .clientSecretExpiresAt(LocalDateTime.now().plusYears(10L).toInstant(ZoneOffset.UTC))
            .clientName(CommonConstants.DEFAULT_INTERNAL_SERVER_CLIENT_CLIENT_ID)
            .clientAuthenticationMethods(
                clientAuthenticationMethods -> {
                  clientAuthenticationMethods.addAll(
                      Arrays.asList(ClientAuthenticationMethod.CLIENT_SECRET_BASIC));
                })
            .authorizationGrantTypes(
                authorizationGrantTypes -> {
                  authorizationGrantTypes.addAll(
                      Arrays.asList(AuthorizationGrantType.CLIENT_CREDENTIALS));
                })
            .redirectUri(moduleCommonSecurityProperties.getOauth2RedirectUri())
            .scopes(
                scopes -> {
                  scopes.addAll(
                      Arrays.asList("all", Oauth2Constant.CLIENT_SCOPE_INTERNAL_CLIENT_KEY));
                })
            .clientSettings(
                ClientSettings.builder()
                    .requireProofKey(false)
                    .requireAuthorizationConsent(false)
                    .jwkSetUrl(StringUtils.EMPTY)
                    .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)
                    .build())
            .tokenSettings(
                TokenSettings.builder()
                    .accessTokenFormat(OAuth2TokenFormat.REFERENCE)
                    .reuseRefreshTokens(true)
                    .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
                    .accessTokenTimeToLive(Duration.ofDays(30))
                    .refreshTokenTimeToLive(Duration.ofDays(366))
                    .authorizationCodeTimeToLive(Duration.ofMinutes(5))
                    .build())
            .build();

    registeredClientRepository.save(registeredClient);

    log.info("oauth2 内部服务客户端初始化成功");
  }
}
